<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +-----------------------------------------------------------------
// | 公共函数库(1.0)
// +-----------------------------------------------------------------
// | Copyright: (c) 2016 Genxin, Inc. All Rights Reserved.
// +-----------------------------------------------------------------
// | PHP version: 5.4
// +-----------------------------------------------------------------
// | Encoding: UTF-8
// +-----------------------------------------------------------------
// | Authors: Geng Xin <gengxin55200@126.com>
// +-----------------------------------------------------------------
//
// $Id: $

header("Content-Type:text/html; charset=UTF-8");

//-----------------------------------------------
// 1. 显示处理
//-----------------------------------------------
/**
 * 1.1 日期转换
 *
 * @param $time
 * @param string $format
 * @return bool|string
 */
function timeToDate($time, $format = 'Y-m-d H:i')
{
    if (empty($time) || !isset($time)) {
        return '-';
    }
    $format = str_replace('#', ':', $format);

    return date($format, $time);
}

/**
 * 1.2 货币处理
 *
 * @param $num
 * @return string
 */
function handleMoney($num)
{
    $num = intval($num);
    if ($num <= 100000) {
        $num = preg_replace('/(?<=[0-9])(?=(?:[0-9]{3})+(?![0-9]))/', ',', $num);
        $num2 = '￥' . $num;
    } else {
        $num /= 10000;
        $num2 = sprintf("%.2f", $num);
        $num2 = '￥' . $num2 . '万';
    }
    return $num2;
}

/**
 * 1.3.1 手机号处理
 *
 * @param  $telephone $time
 * @param  string $replace
 * @return string
 */
function handleTelephone($telephone, $replace = '****')
{
    $str1 = substr($telephone, 0, 3);
    $str2 = substr($telephone, 7, 4);

    return $str1 . $replace . $str2;
}

/**
 *  1.3.2 手机号验证
 *
 * @param $phone
 * @return bool
 */
function isPhoneNo($phone)
{
    if (preg_match("/^13[0-9]{1}[0-9]{8}$|15[0189]{1}[0-9]{8}$|18[0-9]{9}$|176[0-9]{8}$/", $phone)) {
        return true;
    } else {
        //手机号码格式不对
        return false;
    }
}

/**
 * 验证身份证号
 *
 * @param $vStr
 * @return bool
 */
function isCreditNo($vStr)
{
    $vCity = array(
        '11', '12', '13', '14', '15', '21', '22',
        '23', '31', '32', '33', '34', '35', '36',
        '37', '41', '42', '43', '44', '45', '46',
        '50', '51', '52', '53', '54', '61', '62',
        '63', '64', '65', '71', '81', '82', '91'
    );

    if (!preg_match('/^([\d]{17}[xX\d]|[\d]{15})$/', $vStr)) return false;

    if (!in_array(substr($vStr, 0, 2), $vCity)) return false;

    $vStr = preg_replace('/[xX]$/i', 'a', $vStr);
    $vLength = strlen($vStr);

    if ($vLength == 18) {
        $vBirthday = substr($vStr, 6, 4) . '-' . substr($vStr, 10, 2) . '-' . substr($vStr, 12, 2);
    } else {
        $vBirthday = '19' . substr($vStr, 6, 2) . '-' . substr($vStr, 8, 2) . '-' . substr($vStr, 10, 2);
    }

    if (date('Y-m-d', strtotime($vBirthday)) != $vBirthday) return false;
    if ($vLength == 18) {
        $vSum = 0;
        for ($i = 17; $i >= 0; $i--) {
            $vSubStr = substr($vStr, 17 - $i, 1);
            $vSum += (pow(2, $i) % 11) * (($vSubStr == 'a') ? 10 : intval($vSubStr, 11));
        }
        if ($vSum % 11 != 1) return false;
    }

    return true;
}

/**
 * 1.4 标题处理
 *
 * @param $str
 * @param $num
 * @param bool $is_chinese
 * @return string
 */
function handleTitle($str, $num, $is_chinese = false)
{
    if ($is_chinese) {
        $newStr = preg_replace('/[^\x{4e00}-\x{9fa5}]/u', '', $str);
    }
    $length = mb_strlen($newStr, "utf-8");
    if ($length > $num) {
        return mb_substr($str, 0, $num, 'utf-8') . '...';
    } else {
        return $str;
    }
}

/**
 * 1.5 默认显示字符串
 *
 * @param $name
 * @param $show
 * @return string
 */
function showDefault($name, $show = '-')
{
    if (isset($name) && !empty($name)) {
        return $name;
    } else {
        return $show;
    }
}

/**
 * 1.6 百分比显示
 *
 * @param $num
 * @return string
 */
function showPrecent($num)
{
    return strval(floor($num * 100)) . '%';
}

/**
 * 1.7 图片绝对路径处理
 *
 * @param $url
 * @return string
 */
function showImage($url)
{
    if ($url) {
        $show = C('SYS_HOST') . $url;
    } else {
        $show = C('SYS_EMPTY_IMG');
    }
    return $show;
}

/**
 * escape 解码(UTF8)
 *
 * @param type $str
 * @return type
 */
function unescape($str)
{
    $ret = '';
    $len = strlen($str);
    for ($i = 0; $i < $len; $i++) {
        if ($str[$i] == '%' && $str[$i + 1] == 'u') {
            $val = hexdec(substr($str, $i + 2, 4));
            if ($val < 0x7f) {
                $ret .= chr($val);
            } else if ($val < 0x800) {
                $ret .= chr(0xc0 | ($val >> 6)) . chr(0x80 | ($val & 0x3f));
            } else {
                $ret .= chr(0xe0 | ($val >> 12)) . chr(0x80 | (($val >> 6) & 0x3f)) . chr(0x80 | ($val & 0x3f));
            }
            $i += 5;
        } else if ($str[$i] == '%') {
            $ret .= urldecode(substr($str, $i, 3));
            $i += 2;
        } else $ret .= $str[$i];
    }
    return $ret;
}

/**
 * js escape php 实现
 *
 * @param $string the sting want to be escaped
 * @param $in_encoding
 * @param $out_encoding
 */
function escape($string, $in_encoding = 'UTF-8', $out_encoding = 'UCS-2')
{
    $return = '';
    if (function_exists('mb_get_info')) {
        for ($x = 0; $x < mb_strlen($string, $in_encoding); $x++) {
            $str = mb_substr($string, $x, 1, $in_encoding);
            if (strlen($str) > 1) { // 多字节字符
                $return .= '%u' . strtoupper(bin2hex(mb_convert_encoding($str, $out_encoding, $in_encoding)));
            } else {
                $return .= '%' . strtoupper(bin2hex($str));
            }
        }
    }
    return $return;
}

//-----------------------------------------------
// 2. 加密处理
//-----------------------------------------------
/**
 * 2.1 字符串加密处理
 *
 * @param $str
 * @param string $mode
 * @param string $salt
 * @return string
 */
function cryptString($str, $mode = 'md5', $salt = '')
{
    if (empty($salt) || !isset($salt)) {
        $salt = C('SYS_SALT');
    }
    switch ($mode) {
        case 'md5':
            $secret = md5($str . $salt);
            break;
        case 'sha1':
            $secret = sha1($str . $salt);
            break;
        case 'crypt':
            $secret = crypt($str , $salt);
            break;
        case 'mixed':
            $secret = md5(crypt($str ,substr($str,0,2)));
            break;
    }
    return $secret;
}

/**
 * 2.2 RSA加密
 *
 * @param $data
 * @return string
 */
function rsaEncrypt($data)
{
    $pub = file_get_contents(RSA_PUBLIC_KEY);
    $pu_key = openssl_pkey_get_public($pub);//这个函数可用来判断公钥是否是可用
    $result = '';
    for ($i = 0; $i < strlen($data) / 100; $i++) {
        $str = substr($data, $i * 100, 100);
        openssl_public_encrypt($str, $encrypted, $pu_key);//公钥加密
        $result .= $encrypted;
    }
    openssl_free_key($pu_key);
    return base64_encode($result);
}

/**
 * 2.3 RSA解密
 *
 * @param $data
 * @return string
 */
function rsaDecrypt($data)
{
    $result = '';
    $pi = file_get_contents(RSA_PRIVATE_KEY);
    $pi_key = openssl_pkey_get_private($pi);
    $d = base64_decode($data);
    for ($i = 0; $i < strlen($d) / 128; $i++) {
        $str = substr($d, $i * 128, 128);
        openssl_private_decrypt($str, $decrypt, $pi_key);
        $result .= $decrypt;
    }
    openssl_free_key($pi_key);
    return $result;
}

//-----------------------------------------------
// 3. 内容处理
//-----------------------------------------------
/**
 * 1.1.1 字符串过滤
 *
 * @param $dirty_html 需要过滤的字符串
 * @return string 返回的字符串
 */
function contentFilter($dirty_html)
{
    vendor('Htmlpurifier.library.HTMLPurifier#auto');

    $config = HTMLPurifier_Config::createDefault();
    $config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
    $config->set('Core.Encoding', 'UTF-8');
    $config->set('AutoFormat.RemoveEmpty', true);

    $purifier = new HTMLPurifier($config);
    $clean_html = $purifier->purify($dirty_html);
    $clean_html = addslashes($clean_html);

    return $clean_html;
}

/**
 * 1.1.2  字符串显示
 * 使用此方法显示，否则直接显示HTML源码
 *
 * @param $str
 * @return string
 */
function contentShow($str)
{
//    $str = str_replace(PHP_EOL, '', $str);
    $str= htmlspecialchars_decode($str);
    $str = stripslashes($str);

    return $str;
}

/**
 * 1.2 跨站点攻击(过时)
 * 字符串参数，可能包含恶意的脚本代码如<script language="javascript">alert("hello world");</script>
 * @param $val
 * @return mixed
 */
function removeXSS($val) {
    $val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
    $search = 'abcdefghijklmnopqrstuvwxyz';
    $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $search .= '1234567890!@#$%^&*()';
    $search .= '~`";:?+/={}[]-_|\'\\';
    for ($i = 0; $i < strlen($search); $i++) {
        // ;? matches the ;, which is optional
        // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars

        // @ @ search for the hex values
        $val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
        // @ @ 0{0,7} matches '0' zero to seven times
        $val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
    }

    // now the only remaining whitespace attacks are \t, \n, and \r
    $ra1 = array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
    $ra2 = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
    $ra = array_merge($ra1, $ra2);

    $found = true; // keep replacing as long as the previous round replaced something
    while ($found == true) {
        $val_before = $val;
        for ($i = 0; $i < sizeof($ra); $i++) {
            $pattern = '/';
            for ($j = 0; $j < strlen($ra[$i]); $j++) {
                if ($j > 0) {
                    $pattern .= '(';
                    $pattern .= '(&#[xX]0{0,8}([9ab]);)';
                    $pattern .= '|';
                    $pattern .= '|(&#0{0,8}([9|10|13]);)';
                    $pattern .= ')*';
                }
                $pattern .= $ra[$i][$j];
            }
            $pattern .= '/i';
            $replacement = substr($ra[$i], 0, 2) . '<x>' . substr($ra[$i], 2); // add in <> to nerf the tag
            $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
            if ($val_before == $val) {
                // no replacements were made, so exit the loop
                $found = false;
            }
        }
    }
    return $val;
}

/**
 * 2.3 黑/白名单过滤
 *
 * @param $str 需要过滤的字符串
 * @return string 返回的字符串
 */
function blackWordFilter($str)
{
    // 黑名单
    $badwords_path = FILE_UPLOAD_PATH . '/Public/doc/badwords.txt';
    $badwords = file_get_contents($badwords_path);
    $badwords = explode('@@',$badwords);
    $black_list = array();
    foreach($badwords as $val){
        if(trim($val)){
            $black_list[] = trim($val);
        }
    }
    // 白名单
    $whitewords_path = FILE_UPLOAD_PATH . '/Public/doc/whitewords.txt';
    $whitewords = file_get_contents($whitewords_path);
    $whitewords = explode('@@',$whitewords);
    $white_list = array();
    foreach($whitewords as $val2){
        if(trim($val2)){
            $white_list[] = trim($val2);
        }
    }
    $obj = new \Org\Util\BadwordFilter($white_list, $black_list);

    return $obj->replace($str);
}

/**
 * 2.4 汉字转拼音
 *
 * @param $s
 * @param bool $isfirst
 * @return string
 */
function pingyin($s, $isfirst = false)
{
    static $pinyins;

    $s = trim($s);
    $len = strlen($s);
    if ($len < 3) return $s;

    if (!isset($pinyins)) {
        $pinyin_path = FILE_UPLOAD_PATH . '/Public/doc/pinyin.txt';
        $data = file_get_contents($pinyin_path);
        $a1 = explode('|', $data);
        $pinyins = array();
        foreach ($a1 as $v) {
            $a2 = explode(':', $v);
            $pinyins[$a2[0]] = $a2[1];
        }
    }

    $rs = '';
    for ($i = 0; $i < $len; $i++) {
        $o = ord($s[$i]);
        if ($o < 0x80) {
            if (($o >= 48 && $o <= 57) || ($o >= 97 && $o <= 122)) {
                $rs .= $s[$i]; // 0-9 a-z
            } elseif ($o >= 65 && $o <= 90) {
                $rs .= strtolower($s[$i]); // A-Z
            } else {
                $rs .= '_';
            }
        } else {
            $z = $s[$i] . $s[++$i] . $s[++$i];
            if (isset($pinyins[$z])) {
                $rs .= $isfirst ? $pinyins[$z][0] : $pinyins[$z];
            } else {
                $rs .= '_';
            }
        }
    }
    return $rs;
}

//-----------------------------------------------
// 4. 生成函数
//-----------------------------------------------
/**
 * 4.1 生成id(有id)
 *
 * @param $id
 * @param $prefix
 * @return string
 */
function generalId($id, $prefix = 'xsb_')
{
    $str = time();
    $gid = $prefix . substr(strval($str), -6) . $id;
    return $gid;
}

/**
 * 4.2 生成id(无id)
 *
 * @param $prefix
 * @return string
 */
function generalId2($prefix = 'XSB')
{
    list($usec, $sec) = explode(' ', microtime());
    $num = ((float)$usec + (float)$sec) * 10000;
    $num = strval($num);
    return $prefix . $num;
}

/**
 * 4.3 返回随机数
 *
 * @return int
 */
function generalRandomNumber()
{
    return rand(10000000, 99999999) . time();
}

/**
 * 4.4 生成随机验证码
 *
 * @param int $length 验证码的长度
 * @param int $level 复杂度
 * @return type 复杂度级别
 */
function generalRandomString($length = 6, $level = 1)
{
    $str1 = '0123456789';
    $str2 = $str1 . 'abcdefghijklmnopqrstuvwxyz';
    $str3 = $str2 . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    switch ($level) {
        case 1:
            $rs = substr(str_shuffle($str1), 0, $length);
            break;
        case 2:
            $rs = substr(str_shuffle($str2), 0, $length);
            break;
        case 3:
            $rs = substr(str_shuffle($str3), 0, $length);
            break;
        default:
            break;
    }
    return $rs;
}

/**
 * 4.5 生成一个4位数，不足补零(模仿matlab)
 *
 * @param $num
 * @param bool $mode
 * @return string
 */
function generalNum2char($num, $mode = true)
{
    $char = array('零','一','二','三','四','五','六','七','八','九');
    $dw = array('','十','百','千','','万','亿','兆');
    $dec = '点';
    $retval = '';
    if ($mode) {
        preg_match_all('/^0*(\d*)\.?(\d*)/', $num, $ar);
    } else {
        preg_match_all('/(\d*)\.?(\d*)/', $num, $ar);
    }
    if ($ar[2][0] != '') {
        $retval = $dec . ch_num($ar[2][0], false); // 如果有小数，先递归处理小数
    }
    if ($ar[1][0] != '') {
        $str = strrev($ar[1][0]);
        for ($i = 0; $i < strlen($str); $i++) {
            $out[$i] = $char[$str[$i]];
            if ($mode) {
                $out[$i] .= $str[$i] != '0' ? $dw[$i % 4] : '';
                if ($str[$i] + $str[$i - 1] == 0) {
                    $out[$i] = '';
                }
                if ($i % 4 == 0) {
                    $out[$i] .= $dw[4 + floor($i / 4)];
                }
            }
        }
        $retval = join('', array_reverse($out)) . $retval;
    }
    return $retval;
}

//-----------------------------------------------
// 5 其他
//-----------------------------------------------
/**
 * 5.1 返回Unix时间戳
 *
 * @param $time
 * @return int
 */
function timeToUnix($time)
{
    return strtotime($time);
}

/**
 * 5.2 设置换行
 *
 * @param $string
 * @return mixed
 */
function setBreakline($string)
{
    return str_replace(array('<br/>', '<br />'), array("\n", "\n"), $string);
}

/**
 * 5.3 计算字符串长度
 *
 * @param unknown $str
 * @return number
 */
function calculateUtf8Length($str)
{
    $count = 0;
    for ($i = 0; $i < strlen($str); $i++) {
        $value = ord($str[$i]);
        if ($value > 127) {
            $count++;
            if ($value >= 192 && $value <= 223) {
                $i++;
            } elseif ($value >= 224 && $value <= 239) {
                $i = $i + 2;
            } elseif ($value >= 240 && $value <= 247) {
                $i = $i + 3;
            } else {
                die('Not a UTF-8 compatible string');
            }
        }
        $count++;
    }
    return $count;
}

/**
 * 5.4 计算字节数
 *
 * @param int $size 验证码的长度
 * @return type
 */
function calculateBytes($size)
{
    $units = array(' B', ' KB', ' MB', ' GB', ' TB');
    for ($i = 0; $size >= 1024 && $i < 4; $i++)
        $size /= 1024;
    return round($size, 2) . $units[$i];
}

/**
 * 5.5 将对象转换为数组
 *
 * @param $d
 * @return array
 */
function objectToArray($d)
{
    if (is_object($d)) {
        $d = get_object_vars($d);
    }
    if (is_array($d)) {
        return array_map(__FUNCTION__, $d);
    } else {
        return $d;
    }
}

/**
 * 无限极分类
 * 递归重组节点信息多维数组(http://www.itnose.net/news/148/6205812)
 *
 * @param  [array] $node [要处理的节点数组:二维数组]
 * @param  [int]   $pid  [父级ID]
 * @return [array]       [树状结构的节点体系:多维数组]
 */
function nodeMerge($node, $pid = '0')
{
    $arr = array();
    foreach ($node as $v) {
        if ($v['pid'] == $pid) {
            $v['child'] = nodeMerge($node, $v['id']);
            $arr[] = $v;
        }
    }
    return $arr;
}

/**
 * 一次性取出多少个数
 * random.org 是一个随机数的种子，有免费和收费两种,这里使用的是sequences
 *
 * @param $max_num
 * @param $length
 * @return array|int
 */
function topN($max_num, $length)
{
    $url = 'https://www.random.org/sequences/?min=1&max=' . $max_num . '&col=1&format=plain&rnd=new';
    $ch = curl_init();
    curl_setopt_array($ch,
        array(
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_SSL_VERIFYPEER => false,
        )
    );
    $res = curl_exec($ch);
    if (curl_errno($ch)) {
        echo 'error no is: ' , curl_errno($ch) , 'and error is ' , curl_error($ch);
        return -1;
    }

    $res_arr = preg_split('/\n/', $res, -1, PREG_SPLIT_NO_EMPTY);
    $winner = array_slice($res_arr, 0, $length); // 从数组中取出一段
    curl_close($ch);

    return $winner;
}

/**
 *获取ip
 *
 * @return string
 */
function getIp()
{
    // getenv: PHP 获取环境变量
    if (getenv('HTTP_CLIENT_IP')) {
        $ip = getenv('HTTP_CLIENT_IP');
    } else if(getenv('HTTP_X_FORWARDED_FOR')) {
        $ip = getenv('HTTP_X_FORWARDED_FOR');
    } else if(getenv('REMOTE_ADDR')) {
        $ip = getenv('REMOTE_ADDR');
    } else {
        $ip = 'unknow';
    }
    return $ip;
}

/**
 * 创建目录
 *
 * @param $dir
 * @return bool
 */
function create_folders($dir)
{
    return is_dir($dir) or (create_folders(dirname($dir)) and mkdir($dir, 0777) and @touch($dir.'/index.html'));
}